#!/usr/bin/env python2
#
#	Service Monitor
#	Copyright (c) 2013. by Way2CU
#	Author: Mladen Mijatov
#	
#	This program is free software; you can redistribute it and/or modify
#	it under the terms of the GNU General Public License as published by
#	the Free Software Foundation; either version 3 of the License, or
#	(at your option) any later version.
#	
#	This program is distributed in the hope that it will be useful,
#	but WITHOUT ANY WARRANTY; without even the implied warranty of
#	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#	GNU General Public License for more details.
#	
#	You should have received a copy of the GNU General Public License
#	along with this program; if not, write to the Free Software
#	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#	MA 02110-1301, USA.

import os
import sys
import json
import time
import curses
import locale
import math

from shared import colors
from shared.monitor import Monitor
from shared.page_bar import PageBar
from monitors.seo_monitor import SeoMonitor
from monitors.dns_monitor import DnsMonitor
from monitors.disk_monitor import DiskMonitor


class ServiceMonitor:
	"""Service monitor is a flexible system for monitoring various services on big screens."""
	CHAR_Q = 113
	CHAR_1 = 49
	CHAR_2 = 50
	CHAR_3 = 51
	CHAR_4 = 52
	CHAR_5 = 53
	CHAR_6 = 54
	CHAR_7 = 55
	CHAR_8 = 56
	CHAR_9 = 57

	def __init__(self):
		self._screen = None
		self._window = None
		self._window_position = None
		self._window_size = None
		self._page_bar = None
		self._threads = []
		self._pages = []
		self._active_threads = 0

		# initialize display
		locale.setlocale(locale.LC_ALL, '')
		self._initialize_curses()

		# create monitors
		if not self._create_monitors():
			# if init failed exit normally
			self._terminate()

		else:
			# monitors created, enter main loop
			self._switch_page(1)
			self._main_loop()

	def _main_loop(self):
		"""Application main loop"""
		self._window.erase()
		while True:
			try:
				char = self._screen.getch()
				time.sleep(0.2)

			except KeyboardInterrupt:
				char = self.CHAR_Q

			if char == self.CHAR_Q:
				# quit
				self._terminate()
				break

			elif char >= self.CHAR_1 and char <= self.CHAR_9:
				# handle page switching
				page = char - self.CHAR_1 + 1
				self._switch_page(page)

			curses.doupdate()

	def _terminate(self):
		"""Stop all threads and exit"""
		self._cleanup_curses()

		# notify all threads
		for page, thread in self._threads:
			thread.notify_terminate()

		# give threads 5 seconds to terminate
		for seconds in range(0, 5):
			time.sleep(1)
			if self._active_threads == 0:
				break

		# exit gracefully
		sys.exit(0)

	def _initialize_curses(self):
		"""Initialize and configure curses library"""
		# initialize curses
		self._screen = curses.initscr()
		self._screen.nodelay(1)

		# initialize colors
		colors.initialize()

		# configure curses
		curses.noecho()
		curses.cbreak()

	def _cleanup_curses(self):
		"""Restore terminal settings"""
		curses.echo()
		curses.nocbreak()
		curses.endwin()

	def _create_monitors(self):
		"""Create monitors and pages"""
		config = None
		result = False
		decoder = json.JSONDecoder()
		config_file = Monitor.get_config_filename('config.json')

		# decode JSON config
		if os.path.exists(config_file):
			with open(config_file, 'r') as raw_config:
				config = decoder.decode(raw_config.read())

		# create monitors
		if config is not None:
			# create page switcher if needed
			if config['show_page_bar']:
				screen_size = self._screen.getmaxyx()
				page_bar_window = self._screen.derwin(1, screen_size[1], 0, 0)
				self._window = self._screen.derwin(screen_size[0] - 1, screen_size[1], 1, 0)
				self._page_bar = PageBar(self, page_bar_window)

			else:
				self._window = self._screen

			# cache geometry
			self._window_size = self._window.getmaxyx()
			self._window_position = self._window.getyx()

			# create monitor
			for index, page in enumerate(config['pages'], 1):
				self._pages.append(page['title'])

				for monitor in page['monitors']:
					geometry = monitor['size'] + monitor['position']
					class_name = monitor['class']
					config_file = monitor['config']

					# create new monitor
					if class_name in globals():
						# create window and thread
						try:
							window = self._window.derwin(*geometry)
							thread = globals()[class_name](window, config_file)

						except Exception as error:
							sys.stdout.write('Error creating monitor "{0}": {1}"\n'.format(class_name, str(error)))
							time.sleep(5)
							continue

						# add new monitor thread to local storage
						self._threads.append((index, thread))
						self._active_threads += 1

						# start new thread
						thread.start()

			result = True

		return result

	def _switch_page(self, new_page):
		"""Switch to specified page"""
		self._window.erase()
		self._window.noutrefresh()

		for page, monitor in self._threads:
			if page == new_page:
				monitor.show()

			else:
				monitor.hide()

		# update page bar
		if self._page_bar is not None:
			self._page_bar.notify_page_change(new_page)


if __name__ == '__main__':
	ServiceMonitor()
